package migrate

import (
	"context"
	"fmt"
	"gitee.com/hexug/go-tools/logger"
	"gitee.com/hexug/kube-sync/apps/resource"
	appsv1 "k8s.io/api/apps/v1"
	batchv1 "k8s.io/api/batch/v1"
	batchv1beta1 "k8s.io/api/batch/v1beta1"
	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/json"
	"k8s.io/client-go/kubernetes"
)

// PrintK8sVersionAndNamespaces 打印 K8s 版本和所有命名空间
func PrintK8sVersionAndNamespaces(ctx context.Context, clientset *kubernetes.Clientset) {
	// 获取并打印 Kubernetes 版本
	version, err := clientset.Discovery().ServerVersion()
	if err != nil {
		logger.L().Warnf("[WARN] 获取 Kubernetes 版本失败: %v", err)
	} else {
		fmt.Printf("Kubernetes 版本: %s\n", version.String())
	}

	// 获取并打印命名空间列表
	nsList, err := clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
	if err != nil {
		logger.L().Warnf("[WARN] 获取命名空间列表失败: %v\n", err)
		return
	}

	fmt.Println("命名空间列表:")
	for _, ns := range nsList.Items {
		fmt.Printf(" - %s\n", ns.Name)
	}
}

func (s *WorkloadMigrator) deployResources(ctx context.Context, req *requestDeployResources) error {
	//PrintK8sVersionAndNamespaces(ctx, req.clientSet)
	res, err := s.toCreateResourceRequest(req)
	if err != nil {
		return err
	}
	resCns := resource.NewCheckNamespaceRequest()
	resCns.AddNamespace(s.destNamespaces...)
	resCns.ClientSet = s.destClientSet
	err = s.dpsvc.CreateNamespace(ctx, resCns)
	if err != nil {
		logger.L().Errorw("create namespace failed", "err", err)
		return err
	}
	err = s.dpsvc.CreateResource(ctx, res)
	if err != nil {
		return err
	}
	return nil
}

func (s *WorkloadMigrator) toCreateResourceRequest(req *requestDeployResources) (*resource.CreateResourceRequest, error) {
	res := resource.NewCreateResourceRequest()
	res.ClientSet = req.clientSet
	if req.resCM != nil {
		for key, cmInfo := range req.resCM.Items {
			for _, cm := range cmInfo {
				jsonData, err := json.Marshal(cm.Data)
				if err != nil {
					logger.L().Errorw("failed to marshal map to JSON", "err", err, "key", key, "Kind", cm.ResourcesKind, "Namespace", cm.Namespace)
					continue
				}
				if cm.ResourcesKind == "ConfigMap" {
					configMap := &corev1.ConfigMap{}
					if err = UnmarshalTo(jsonData, configMap); err != nil {
						logger.L().Errorw("failed to unmarshal map to ConfigMap",
							"err", err, "key", key,
							"Kind", cm.ResourcesKind,
							"Name", cm.WorkloadName,
							"Namespace", cm.Namespace)
						continue
					}
					if configMap.Name == "" {
						logger.L().Warnw("empty ConfigMap after unmarshal, skip",
							"key", key, "Kind", cm.ResourcesKind,
							"Name", cm.WorkloadName,
							"Namespace", cm.Namespace)
						continue
					}
					res.AddResource(key, cm.Namespace, cm.ResourcesKind, configMap.APIVersion, configMap.Name, configMap)
				} else {
					logger.L().Errorw("unknown configmap type",
						"Kind", cm.ResourcesKind,
						"Name", cm.WorkloadName,
						"Namespace", cm.Namespace)
				}
			}
		}
	}
	if req.resSecret != nil {
		for key, sCore := range req.resSecret.Items {
			for _, s := range sCore.Secrets {
				sData, err := json.Marshal(s)
				if err != nil {
					logger.L().Errorw("failed to marshal map to JSON", "err", err, "key", key, "Kind", sCore.ResourcesKind, "Namespace", sCore.Namespace)
					continue
				}
				secret := &corev1.Secret{}
				if err = UnmarshalTo(sData, secret); err != nil {
					logger.L().Errorw("failed to unmarshal map to Secret",
						"err", err, "key", key,
						"Kind", sCore.ResourcesKind,
						"Namespace", sCore.Namespace)
					continue
				}
				if secret.Name == "" {
					logger.L().Warnw("empty Secret after unmarshal, skip",
						"key", key, "Kind", sCore.ResourcesKind,
						"Namespace", sCore.Namespace)
					continue
				}
				res.AddResource(key, sCore.Namespace, sCore.ResourcesKind, secret.APIVersion, secret.Name, secret)
			}
		}
	}
	if req.resPvPvc != nil {
		for key, pvpvc := range req.resPvPvc.Items {
			ns, _, _, err := ParseResourceKey(key)
			if err != nil {
				logger.L().Errorw("failed to parse resource key", "err", err, "key", key)
				continue
			}
			for _, pv := range pvpvc.PVs {
				pvData, err := json.Marshal(pv)
				if err != nil {
					logger.L().Errorw("failed to marshal map to JSON", "err", err, "key", key, "Kind", "PersistentVolume")
					continue
				}
				pv1 := &corev1.PersistentVolume{}
				if err = UnmarshalTo(pvData, pv1); err != nil {
					logger.L().Errorw("failed to unmarshal map to PersistentVolume",
						"err", err, "key", key,
						"Kind", "PersistentVolume")
					continue
				}
				if pv1.Name == "" {
					logger.L().Warnw("empty PersistentVolume after unmarshal, skip",
						"key", key, "Kind", "PersistentVolume")
					continue
				}
				res.AddResource(key, "", "PersistentVolume", pv1.APIVersion, pv1.Name, pv1)
			}
			for _, pvc := range pvpvc.PVCs {
				pvcData, err := json.Marshal(pvc)
				if err != nil {
					logger.L().Errorw("failed to marshal map to JSON", "err", err, "key", key, "Kind", "PersistentVolumeClaim", "Namespace", ns)
					continue
				}
				pvc1 := &corev1.PersistentVolumeClaim{}
				if err = UnmarshalTo(pvcData, pvc1); err != nil {
					logger.L().Errorw("failed to unmarshal map to PersistentVolumeClaim",
						"err", err, "key", key,
						"Kind", "PersistentVolumeClaim",
						"Namespace", ns)
					continue
				}
				if pvc1.Name == "" {
					logger.L().Warnw("empty PersistentVolumeClaim after unmarshal, skip",
						"key", key, "Kind", "PersistentVolumeClaim",
						"Namespace", ns)
					continue
				}
				res.AddResource(key, ns, "PersistentVolumeClaim", pvc1.APIVersion, pvc1.Name, pvc1)
			}
		}
	}
	if req.resRBAC != nil {
		for key, rbacCore := range req.resRBAC.Items {
			ns, _, _, err := ParseResourceKey(key)
			if err != nil {
				logger.L().Errorw("failed to parse resource key", "err", err, "key", key)
				continue
			}
			// 处理sa
			{
				saData, err := json.Marshal(rbacCore.ServiceAccount)
				if err != nil {
					logger.L().Errorw("failed to marshal map to JSON", "err", err, "key", key, "Kind", "ServiceAccount", "Namespace", ns)
					continue
				}
				sa := &corev1.ServiceAccount{}
				if err = UnmarshalTo(saData, sa); err != nil {
					logger.L().Errorw("failed to unmarshal map to ServiceAccount",
						"err", err, "key", key,
						"Kind", "ServiceAccount",
						"Namespace", ns)
					continue
				}
				if sa.Name == "" {
					logger.L().Warnw("empty ServiceAccount after unmarshal, skip",
						"key", key, "Kind", "ServiceAccount",
						"Namespace", ns)
					continue
				}
				res.AddResource(key, ns, "ServiceAccount", sa.APIVersion, sa.Name, sa)
			}

			// 处理rolebinding
			for _, rb := range rbacCore.RoleBindings {
				rbData, err := json.Marshal(rb)
				if err != nil {
					logger.L().Errorw("failed to marshal map to JSON", "err", err, "key", key, "Kind", "RoleBinding", "Namespace", ns)
					continue
				}
				roleBinding := &rbacv1.RoleBinding{}
				if err = UnmarshalTo(rbData, roleBinding); err != nil {
					logger.L().Errorw("failed to unmarshal map to RoleBinding",
						"err", err, "key", key,
						"Kind", "RoleBinding",
						"Namespace", ns)
					continue
				}
				if roleBinding.Name == "" {
					logger.L().Warnw("empty RoleBinding after unmarshal, skip",
						"key", key, "Kind", "RoleBinding",
						"Namespace", ns)
					continue
				}
				res.AddResource(key, ns, "RoleBinding", roleBinding.APIVersion, roleBinding.Name, roleBinding)
			}

			// 处理clusterRoleBinding
			for _, crb := range rbacCore.ClusterRBs {
				crbData, err := json.Marshal(crb)
				if err != nil {
					logger.L().Errorw("failed to marshal map to JSON", "err", err, "key", key, "Kind", "ClusterRoleBinding")
					continue
				}
				clusterRoleBinding := &rbacv1.ClusterRoleBinding{}
				if err = UnmarshalTo(crbData, clusterRoleBinding); err != nil {
					logger.L().Errorw("failed to unmarshal map to ClusterRoleBinding",
						"err", err, "key", key,
						"Kind", "ClusterRoleBinding")
					continue
				}
				if clusterRoleBinding.Name == "" {
					logger.L().Warnw("empty ClusterRoleBinding after unmarshal, skip",
						"key", key, "Kind", "ClusterRoleBinding")
					continue
				}
				res.AddResource(key, "", "ClusterRoleBinding", clusterRoleBinding.APIVersion, clusterRoleBinding.Name, clusterRoleBinding)
			}

			// 处理role
			for _, r := range rbacCore.Roles {
				rData, err := json.Marshal(r)
				if err != nil {
					logger.L().Errorw("failed to marshal map to JSON", "err", err, "key", key, "Kind", "Role", "Namespace", ns)
					continue
				}
				role := &rbacv1.Role{}
				if err = UnmarshalTo(rData, role); err != nil {
					logger.L().Errorw("failed to unmarshal map to Role",
						"err", err, "key", key,
						"Kind", "Role",
						"Namespace", ns)
					continue
				}
				if role.Name == "" {
					logger.L().Warnw("empty Role after unmarshal, skip",
						"key", key, "Kind", "Role",
						"Namespace", ns)
					continue
				}
				res.AddResource(key, ns, "Role", role.APIVersion, role.Name, role)
			}

			// 处理clusterRole
			for _, cr := range rbacCore.ClusterRoles {
				crData, err := json.Marshal(cr)
				if err != nil {
					logger.L().Errorw("failed to marshal map to JSON", "err", err, "key", key, "Kind", "ClusterRole")
					continue
				}
				clusterRole := &rbacv1.ClusterRole{}
				if err = UnmarshalTo(crData, clusterRole); err != nil {
					logger.L().Errorw("failed to unmarshal map to ClusterRole",
						"err", err, "key", key,
						"Kind", "ClusterRole")
					continue
				}
				if clusterRole.Name == "" {
					logger.L().Warnw("empty ClusterRole after unmarshal, skip",
						"key", key, "Kind", "ClusterRole")
					continue
				}
				res.AddResource(key, "", "ClusterRole", clusterRole.APIVersion, clusterRole.Name, clusterRole)
			}
		}
	}
	if req.resDep != nil {
		for key, workload := range req.resDep.Items {
			jsonData, err := json.Marshal(workload.Data)
			if err != nil {
				logger.L().Errorw("failed to marshal map to JSON", "err", err, "key", key, "Kind", workload.WorkloadType, "Name", workload.WorkloadName, "Namespace", workload.Namespace)
				continue
			}
			switch workload.WorkloadType {
			case "Deployment":
				deploy := &appsv1.Deployment{}
				if err = UnmarshalTo(jsonData, deploy); err != nil {
					logger.L().Errorw("failed to unmarshal map to JSON", "err", err, "key", key, "Kind", workload.WorkloadType, "Name", workload.WorkloadName, "Namespace", workload.Namespace)
					continue
				}
				// 关键检查：是否真的解出了有效对象
				if deploy.Name == "" {
					logger.L().Warnw("empty Deployment after unmarshal, skip",
						"key", key, "Kind", workload.WorkloadType,
						"Name", workload.WorkloadName,
						"Namespace", workload.Namespace)
					continue
				}
				res.AddResource(key, workload.Namespace, workload.WorkloadType, deploy.APIVersion, deploy.Name, deploy)
			case "StatefulSet":
				sts := &appsv1.StatefulSet{}
				if err = UnmarshalTo(jsonData, sts); err != nil {
					logger.L().Errorw("failed to unmarshal map to StatefulSet",
						"err", err, "key", key,
						"Kind", workload.WorkloadType,
						"Name", workload.WorkloadName,
						"Namespace", workload.Namespace)
					continue
				}
				if sts.Name == "" {
					logger.L().Warnw("empty StatefulSet after unmarshal, skip",
						"key", key, "Kind", workload.WorkloadType,
						"Name", workload.WorkloadName,
						"Namespace", workload.Namespace)
					continue
				}
				res.AddResource(key, workload.Namespace, workload.WorkloadType, sts.APIVersion, sts.Name, sts)
			case "DaemonSet":
				ds := &appsv1.DaemonSet{}
				if err = UnmarshalTo(jsonData, ds); err != nil {
					logger.L().Errorw("failed to unmarshal map to DaemonSet",
						"err", err, "key", key,
						"Kind", workload.WorkloadType,
						"Name", workload.WorkloadName,
						"Namespace", workload.Namespace)
					continue
				}
				if ds.Name == "" {
					logger.L().Warnw("empty DaemonSet after unmarshal, skip",
						"key", key, "Kind", workload.WorkloadType,
						"Name", workload.WorkloadName,
						"Namespace", workload.Namespace)
					continue
				}
				res.AddResource(key, workload.Namespace, workload.WorkloadType, ds.APIVersion, ds.Name, ds)

			case "Job":
				job := &batchv1.Job{}
				if err = UnmarshalTo(jsonData, job); err != nil {
					logger.L().Errorw("failed to unmarshal map to Job",
						"err", err, "key", key,
						"Kind", workload.WorkloadType,
						"Name", workload.WorkloadName,
						"Namespace", workload.Namespace)
					continue
				}
				if job.Name == "" {
					logger.L().Warnw("empty Job after unmarshal, skip",
						"key", key, "Kind", workload.WorkloadType,
						"Name", workload.WorkloadName,
						"Namespace", workload.Namespace)
					continue
				}
				res.AddResource(key, workload.Namespace, workload.WorkloadType, job.APIVersion, job.Name, job)

			case "CronJob":
				// 优先尝试 batch/v1
				cronV1 := &batchv1.CronJob{}
				if err = UnmarshalTo(jsonData, cronV1); err == nil {
					if cronV1.Name == "" {
						logger.L().Warnw("empty CronJob (batch/v1) after unmarshal, skip",
							"key", key, "Kind", workload.WorkloadType,
							"Name", workload.WorkloadName, "Namespace", workload.Namespace)
						continue
					}
					res.AddResource(key, workload.Namespace, workload.WorkloadType, cronV1.APIVersion, cronV1.Name, cronV1)
					continue
				}
				// fallback: 尝试 batch/v1beta1
				cronBeta := &batchv1beta1.CronJob{}
				if err = UnmarshalTo(jsonData, cronBeta); err != nil {
					logger.L().Errorw("failed to unmarshal map to CronJob (v1 & v1beta1)",
						"err", err, "key", key, "Kind", workload.WorkloadType,
						"Name", workload.WorkloadName, "Namespace", workload.Namespace)
					continue
				}
				if cronBeta.Name == "" {
					logger.L().Warnw("empty CronJob (batch/v1beta1) after unmarshal, skip",
						"key", key, "Kind", workload.WorkloadType,
						"Name", workload.WorkloadName, "Namespace", workload.Namespace)
					continue
				}
				res.AddResource(key, workload.Namespace, workload.WorkloadType, cronBeta.APIVersion, cronBeta.Name, cronBeta)
			default:
				logger.L().Errorw("unknown workload type",
					"Kind", workload.WorkloadType,
					"Name", workload.WorkloadName,
					"Namespace", workload.Namespace)
				continue
			}
		}
	}
	if req.resSvc != nil {
		for key, svc := range req.resSvc.Items {
			for _, sv := range svc {
				jsonData, err := json.Marshal(sv.Data)
				if err != nil {
					logger.L().Errorw("failed to marshal map to JSON", "err", err, "key", key, "Kind", sv.ResourcesKind, "Namespace", sv.Namespace)
					continue
				}
				if sv.ResourcesKind == "Service" {
					service := &corev1.Service{}
					if err = UnmarshalTo(jsonData, service); err != nil {
						logger.L().Errorw("failed to unmarshal map to Service",
							"err", err, "key", key,
							"Kind", sv.ResourcesKind,
							"Name", sv.WorkloadName,
							"Namespace", sv.Namespace)
						continue
					}
					if service.Name == "" {
						logger.L().Warnw("empty Service after unmarshal, skip",
							"key", key, "Kind", sv.ResourcesKind,
							"Name", sv.WorkloadName,
							"Namespace", sv.Namespace)
						continue
					}
					res.AddResource(key, sv.Namespace, sv.ResourcesKind, service.APIVersion, service.Name, service)
				} else {
					logger.L().Errorw("unknown service type",
						"Kind", sv.ResourcesKind,
						"Name", sv.WorkloadName,
						"Namespace", sv.Namespace)
				}
			}
		}
	}
	return res, nil
}
